package regular2MR;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.hadoop.io.Writable;

// Vertex define
class Vertex implements Writable {
	String name;
	String label;
	boolean visit;
	ArrayList<Edge> neighbors;
	ArrayList<Rvec> rvec;

	public Vertex() {
		this.name = "";
		this.label = "";
		this.neighbors = new ArrayList<Edge>();
		this.rvec = new ArrayList<Rvec>();
	}

	public Vertex(String name) {
		this.name = name;
		this.label = "";
		this.neighbors = new ArrayList<Edge>();
		this.rvec = new ArrayList<Rvec>();
	}

	public Vertex(String name, String label) {
		this.name = name;
		this.label = label;
		this.neighbors = new ArrayList<Edge>();
		this.rvec = new ArrayList<Rvec>();
	}

	public String L() {
		return this.label;
	}

	@Override
	public boolean equals(Object obj) {
		boolean check = false;
		if (obj == null)
			return false;
		else if (obj == this)
			return true;
		else {
			if (obj instanceof Vertex && obj.getClass() == this.getClass()) {
				final Vertex t = (Vertex) obj;
				check = this.name.equals(t.name);
			}
		}
		return check;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = (37 * hash) + (null == this.name ? 0 : this.name.hashCode());
		return hash;
	}

	@Override
	public String toString() {
		return name + "," + label;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		// TODO Auto-generated method stub
		name = in.readUTF();
		label = in.readUTF();
		visit = in.readBoolean();
		int sizeNbs = in.readInt();
		for (int i = 0; i < sizeNbs; i++) {
			Edge edge = new Edge();
			edge.readFields(in);
			neighbors.add(edge);
		}

		int sizeR = in.readInt();
		for (int i = 0; i < sizeR; i++) {
			Rvec r = new Rvec();
			r.readFields(in);
			rvec.add(r);
		}
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(name);
		out.writeUTF(label);
		out.writeBoolean(visit);

		out.writeInt(neighbors.size());
		for (Edge edge : neighbors)
			edge.write(out);

		out.writeInt(rvec.size());
		for (Rvec r : rvec)
			r.write(out);

	}
}

class Edge implements Writable {
	String target;

	public Edge() {
		target = null;
	}

	public Edge(String target) {
		this.target = target;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		target = in.readUTF();
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(target);
	}
}

public class Graph {
	ArrayList<Vertex> vertices = new ArrayList<Vertex>();

	public Graph() {
	}

	public Graph(ArrayList<Vertex> V) {
		this.vertices = V;
	}

	public int getSize() {
		return vertices.size();
	}

	public int getIndexOf(Vertex v) {
		return vertices.indexOf(v);
	}
}

class RvecWritable implements Writable {
	ArrayList<Rvec> rvset;

	public RvecWritable() {
		rvset = new ArrayList<Rvec>();
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		int size = in.readInt();
		//System.out.print("size = " + size + "\n");
		for (int i = 0; i < size; i++) {
			Rvec r = new Rvec();
			r.readFields(in);
			rvset.add(r);
			//System.out.print("\ti = " + i + " : ");
			//System.out.print("\t\t(v,u) =" + r.toString() + "\n");
		}
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeInt(rvset.size());
		//System.out.print("size = " + rvset.size() + "\n");
		for (Rvec r : rvset) {
			r.write(out);
			//System.out.print("\t(v,u) = " + r.toString() + "\n");
		}
	}
	
	@Override
	public String toString(){
		String s= "";
		for(Rvec r: rvset)
			s+=r.toString()+"\n";
		return s;
	}
}

class Rvec implements Writable {
	String v;
	String u;
	ArrayList<BoolFormula> value;

	public Rvec() {
		v = "";
		u = "";
		value = new ArrayList<BoolFormula>();
	}

	public Rvec(String v, String u, ArrayList<BoolFormula> value) {
		this.v = v;
		this.u = u;
		this.value = value;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		v = in.readUTF();
		u = in.readUTF();
		int size = in.readInt();
		for (int i = 0; i < size; i++) {
			BoolFormula b = new BoolFormula();
			b.readFields(in);
			value.add(b);
		}
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(v);
		out.writeUTF(u);
		out.writeInt(value.size());
		for (BoolFormula b : value)
			b.write(out);
	}

	@Override
	public boolean equals(Object obj) {
		boolean check = false;
		if (obj == null)
			return false;
		else if (obj == this)
			return true;
		else {
			if (obj instanceof Rvec && obj.getClass() == this.getClass()) {
				final Rvec r = (Rvec) obj;
				check = this.v.equals(r.v) && this.u.equals(r.u);
			}
		}
		return check;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = (37 * hash) + this.v.hashCode() + this.u.hashCode();
		return hash;
	}

	@Override
	public String toString() {
		String s = "(" + v + ", " + u + ", ";
		for (BoolFormula b : value)
			s += b.toString() + " ";
		s += ")";
		return s;
	}

}

class BoolFormula implements Writable {
	boolean value;
	X boolVar;

	public BoolFormula() {
		value = false;
		boolVar = null;
	}

	public BoolFormula(boolean value) {
		this.value = value;
	}

	public BoolFormula(X boolVar) {
		this.boolVar = boolVar;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		value = in.readBoolean();
		boolean check = in.readBoolean();
		if (check){
			X tmp = new X();
			tmp.readFields(in);
			boolVar = tmp;
		}			
		else
			boolVar = null;
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeBoolean(value);
		if (boolVar == null) {
			out.writeBoolean(false);
		} else {
			out.writeBoolean(true);
			boolVar.write(out);			
		}
	}
	
	public boolean equals(Object obj) {
		boolean check = false;
		if (obj == null)
			return false;
		else if (obj == this)
			return true;
		else {
			if (obj instanceof BoolFormula && obj.getClass() == this.getClass()) {
				final BoolFormula b = (BoolFormula) obj;
				check = this.boolVar.equals(b.boolVar);
			}
		}
		return check;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = (37 * hash) + this.boolVar.hashCode();
		return hash;
	}


	@Override
	public String toString() {
		if (boolVar == null)
			return value + "";
		else
			return boolVar.toString();
	}

}

class X implements Writable {
	String v;
	String u;

	public X() {
		v = "";
		u = "";
	}

	public X(String v, String u) {
		this.v = v;
		this.u = u;
	}
	
	public boolean equals(Object obj) {
		boolean check = false;
		if (obj == null)
			return false;
		else if (obj == this)
			return true;
		else {
			if (obj instanceof X && obj.getClass() == this.getClass()) {
				final X x = (X) obj;
				check = this.v.equals(x.v) && this.u.equals(x.u);
			}
		}
		return check;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = (37 * hash) + this.v.hashCode() + this.u.hashCode();
		return hash;
	}


	@Override
	public String toString() {
		return "X(" + v + "," + u + ")";
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		v = in.readUTF();
		u = in.readUTF();
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(v);
		out.writeUTF(u);
	}
}

class NodeType {
	public final static int IN_NODE = 1;
	public final static int OUT_NODE = 2;
	public final static int NODE = 0;
}
